#include "pgm_io.h"

#define err_check(retval,msg) if(retval==0){perror(msg);exit(EXIT_FAILURE);};

//TODO add doxy
int **allocate_dynamic_matrix(int row, int col)
{
  int **ret_val;
  int i;

  ret_val = (int **)malloc(sizeof(int *) * row);
  err_check(ret_val,"memory allocation failure");

  for (i = 0; i < row; ++i) {
    ret_val[i] = (int *)malloc(sizeof(int) * col);
    err_check(ret_val[i],"memory allocation failure");
  }
  return ret_val;
}

//TODO add doxy
int deallocate_dynamic_matrix(int **matrix, int row)
{
  int i;

  for (i = 0; i < row; ++i)
    free(matrix[i]);
  free(matrix);
  return ERROR_OK;
}

//TODO add doxy
void SkipComments(FILE * fp)
{
  int ch;
  char line[100];
  int * retval_int;
  char * retval_char;

  while ((ch = fgetc(fp)) != EOF && isspace(ch))
    ;
  if (ch == '#') {
    retval_char = fgets(line, sizeof(line), fp);
    err_check(retval_char,"Read error...");
    SkipComments(fp);
  } else
    fseek(fp, -1, SEEK_CUR);
}


/** 
 * Reads a PGM P5 file (ONLY P5).
 * 
 * @param filename: Filename with path relative to location of executable.
 * Examples:
 *filename=="pic.pgm"
 *filename=="../imgs/pic.pgm"
 *
 * @param data: Structure to hold readings. Must have memory assigned (excepto for the matrix, since size is not yet known).
 * 
 * @return: data read.
 */
PGMData* readPGM_txt(const char * file_name, PGMData * data)
{
  FILE *pgmFile;
  char version[3];
  int i, j;
  int tmp;
  int retval;
  char * retval_char;

  pgmFile = fopen(file_name, "r");
  if (pgmFile == NULL) {
    perror("cannot open file to read");
    exit(EXIT_FAILURE);
  }

  retval_char = fgets(version, sizeof(version), pgmFile);
  err_check(retval_char,"Read error...");
  if (strcmp(version, "P5")) {
    fprintf(stderr, "Wrong file type!\n");
    exit(EXIT_FAILURE);
  }

  SkipComments(pgmFile);
  retval = fscanf(pgmFile, "%d", &data->col);
  err_check(retval,"Read error...");
  SkipComments(pgmFile);
  retval = fscanf(pgmFile, "%d", &data->row);
  err_check(retval,"Read error...");
  SkipComments(pgmFile);
  retval = fscanf(pgmFile, "%d", &data->max_gray);
  err_check(retval,"Read error...");
  retval = fgetc(pgmFile);
  err_check(retval,"Read error...");

  data->matrix = allocate_dynamic_matrix(data->row, data->col);
  for (i = 0; i < data->row; ++i)
    for (j = 0; j < data->col; ++j){
      retval = fscanf(pgmFile,"%d",&tmp);
      err_check(retval,"Read error...");
      data->matrix[i][j] = tmp;
    }
  
  fclose(pgmFile);
  return data;
}

/** 
 * Reads a PGM P5 file (ONLY P5).
 * 
 * @param filename: Filename with path relative to location of executable.
 * Examples:
 *filename=="pic.pgm"
 *filename=="../imgs/pic.pgm"
 *
 * @param data: Structure to hold readings. Must have memory assigned (excepto for the matrix, since size is not yet known).
 * 
 * @return: data read.
 */
PGMData* readPGM_bin(const char * file_name, PGMData * data){
  FILE *pgmFile;
  char version[3];
  int i, j;
  unsigned char aux;
  int retval;
  char * retval_char;

  pgmFile = fopen(file_name, "rb");
  if (pgmFile == NULL) {
    perror("cannot open file to read");
    exit(EXIT_FAILURE);
  }

  retval_char = fgets(version, sizeof(version), pgmFile);
  err_check(retval_char,"Read error...");
  if (strcmp(version, "P5")) {
    fprintf(stderr, "Wrong file type!\n");
    exit(EXIT_FAILURE);
  }

  SkipComments(pgmFile);
  retval = fscanf(pgmFile, "%d", &data->col);
  err_check(retval,"Read error...");
  SkipComments(pgmFile);
  retval = fscanf(pgmFile, "%d", &data->row);
  err_check(retval,"Read error...");
  SkipComments(pgmFile);
  retval = fscanf(pgmFile, "%d", &data->max_gray);
  err_check(retval,"Read error...");
  retval = fgetc(pgmFile);
  err_check(retval,"Read error...");
  int cnt = 0;
  data->matrix = allocate_dynamic_matrix(data->row, data->col);
  for (i = 0; i < data->row; ++i)
    for (j = 0; j < data->col; ++j){
      retval = fread(&aux,1,1,pgmFile);       	
      err_check(retval,"Read error...");
      data->matrix[i][j] = (int) aux;	
    }
  
  fclose(pgmFile);
  return data; 
}


/** 
 * Writes an image to a PGM P5 file in text mode.
 * 
 * @param filename:
 * Examples:
 *filename=="pic.pgm"
 *
 * @param data: Structure that holds the image to write.
 * 
 * @return: error code.
 */
int writePGM_txt(const char * filename, const PGMData * data)
{
  FILE * pgmFile;
  int i, j;
  int hi, lo;

  pgmFile = fopen(filename, "w");
  if (pgmFile == NULL) {
    perror("cannot open file to write");
    exit(EXIT_FAILURE);
  }

  fprintf(pgmFile, "P5\n");
  fprintf(pgmFile, "%d %d\n", data->col, data->row);
  fprintf(pgmFile, "%d\n", data->max_gray);

  for (i = 0; i < data->row; i++)
    {
      for (j = 0; j < (data->col-1); j++)
      {
	lo = data->matrix[i][j];
	fprintf(pgmFile,"%d ",lo);
      }
      lo = data->matrix[i][j];
      fprintf(pgmFile,"%d\n",lo);
    }

  fclose(pgmFile);
  deallocate_dynamic_matrix(data->matrix, data->row);

  return ERROR_OK;
}

/** 
 * Writes an image to a PGM P5 file in binary mode.
 * 
 * @param filename:
 * Examples:
 *filename=="pic.pgm"
 *
 * @param data: Structure that holds the image to write.
 * 
 * @return: error code.
 */
int writePGM_bin(const char * filename, const PGMData * data)
{
  FILE * pgmFile;
  int i, j;
  int hi, lo;
  unsigned char * aux;

  pgmFile = fopen(filename, "wb");
  if (pgmFile == NULL) {
    perror("cannot open file to write");
    exit(EXIT_FAILURE);
  }
  fprintf(pgmFile,"P5\n%d %d\n%d\n",data->col,data->row,data->max_gray);
  for (i = 0; i < data->row; i++)
    {
      for (j = 0; j < data->col; j++)
      {
	aux = (unsigned char *) &(data->matrix[i][j]);
	fwrite(aux,1,1,pgmFile);
      }
    }
  fclose(pgmFile);
  deallocate_dynamic_matrix(data->matrix, data->row);

  return ERROR_OK;
}
